{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YuXYZBmuHYxa"
      },
      "source": [
        "##### Copyright 2025 Google LLC."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "cellView": "form",
        "id": "Q2fjiWA5HetI"
      },
      "outputs": [],
      "source": [
        "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "juLxdbNPIiPF"
      },
      "source": [
        "# Gemini API: Asynchronous Python requests\n",
        "\n",
        "<a target=\"_blank\" href=\"https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Asynchronous_requests.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" height=30/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dBfCOtXROsVR"
      },
      "source": [
        "This notebook will show you how to make asynchronous and parallel requests using the Gemini API's Python SDK and Python 3's [`asyncio`](https://docs.python.org/3/library/asyncio.html) standard library.\n",
        "\n",
        "The examples here run in Google Colab and use the implicit event loop supplied in Colab. You can also run these commands interactively using the `asyncio` REPL (invoked with `python -m asyncio`), or you can manage the [event loop](https://docs.python.org/3/library/asyncio-eventloop.html) yourself."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "CUCIiuo60vgI"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\u001b[?25l   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/200.0 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m200.0/200.0 kB\u001b[0m \u001b[31m14.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h"
          ]
        }
      ],
      "source": [
        "%pip install -qU 'google-genai>=1.0.0' aiohttp"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g1GCRqAyDOpz"
      },
      "source": [
        "## Set up your API key\n",
        "\n",
        "To run the following cell, your API key must be stored it in a Colab Secret named `GOOGLE_API_KEY`. If you don't already have an API key, or you're not sure how to create a Colab Secret, see the [Authentication ![image](https://storage.googleapis.com/generativeai-downloads/images/colab_icon16.png)](../quickstarts/Authentication.ipynb) quickstart for an example."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "JiXbEhpl_0ya"
      },
      "outputs": [],
      "source": [
        "from google.colab import userdata\n",
        "from google import genai\n",
        "\n",
        "GOOGLE_API_KEY = userdata.get(\"GOOGLE_API_KEY\")\n",
        "client = genai.Client(api_key=GOOGLE_API_KEY)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pzU9xvdMFhNp"
      },
      "source": [
        "Now select the model you want to use in this guide, either by selecting one in the list or writing it down. Keep in mind that some models, like the 2.5 ones are thinking models and thus take slightly more time to respond (cf. [thinking notebook](./Get_started_thinking.ipynb) for more details and in particular learn how to switch the thiking off)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "PQgPpzjsFzRR"
      },
      "outputs": [],
      "source": [
        "MODEL_ID = \"gemini-2.5-flash\" # @param [\"gemini-2.5-flash-lite\", \"gemini-2.5-flash\", \"gemini-2.5-pro\",\"gemini-3-pro-preview\"] {\"allow-input\":true, isTemplate: true}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vtOfRg-UFSuB"
      },
      "source": [
        "## Using local files\n",
        "\n",
        "This simple example shows how can you use local files (presumed to load quickly) with the SDK's `async` API."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "D1Sw3UzEFekL"
      },
      "outputs": [],
      "source": [
        "prompt = \"Describe this image in just 3 words.\"\n",
        "\n",
        "img_filenames = [\"firefighter.jpg\", \"elephants.jpeg\", \"jetpack.jpg\"]\n",
        "img_dir = \"https://storage.googleapis.com/generativeai-downloads/images/\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "w0NBs55iFwyR"
      },
      "source": [
        "Start by downloading the files locally."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "id": "iRGHeraNFwTL"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2025-06-09 09:43:19 URL:https://storage.googleapis.com/generativeai-downloads/images/firefighter.jpg [547369/547369] -> \"firefighter.jpg.1\" [1]\n",
            "2025-06-09 09:43:20 URL:https://storage.googleapis.com/generativeai-downloads/images/elephants.jpeg [224007/224007] -> \"elephants.jpeg.1\" [1]\n",
            "2025-06-09 09:43:20 URL:https://storage.googleapis.com/generativeai-downloads/images/jetpack.jpg [357568/357568] -> \"jetpack.jpg.1\" [1]\n",
            "FINISHED --2025-06-09 09:43:20--\n",
            "Total wall clock time: 0.2s\n",
            "Downloaded: 3 files, 1.1M in 0.02s (56.7 MB/s)\n"
          ]
        }
      ],
      "source": [
        "!wget -nv {img_dir}{{{','.join(img_filenames)}}}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AyUIpSnnJD4W"
      },
      "source": [
        "The async code uses the `aio.models.generate_content` method to invoke the API. Most async API methods can be found in the [`aio`](https://googleapis.github.io/python-genai/genai.html#genai.client.AsyncClient) namespace.\n",
        "\n",
        "Note that this code is not run in parallel. The async call indicates that the event loop *can* yield to other tasks, but there are no other tasks scheduled in this code. This may be sufficient, e.g. if you are running this in a web server request handler as it will allow the handler to yield to other tasks while waiting for the API response."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "AXcB_yPGFlZd"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Boy, cat, tree.\n",
            "Forest elephant family\n",
            "Jetpack Backpack Concept\n"
          ]
        }
      ],
      "source": [
        "import PIL\n",
        "\n",
        "async def describe_local_images():\n",
        "\n",
        "  for img_filename in img_filenames:\n",
        "\n",
        "    img = PIL.Image.open(img_filename)\n",
        "    r = await client.aio.models.generate_content(\n",
        "        model=MODEL_ID,\n",
        "        contents=[prompt, img]\n",
        "    )\n",
        "    print(r.text)\n",
        "\n",
        "\n",
        "await describe_local_images()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Zg4Ki_vKRpV1"
      },
      "source": [
        "## Downloading images asynchronously and in parallel\n",
        "\n",
        "This example shows a more real-world case where an image is downloaded from an external source using the async HTTP library [`aiohttp`](https://pypi.org/project/aiohttp), and each image is processed in parallel."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "id": "RdykoS-G__Tv"
      },
      "outputs": [],
      "source": [
        "import io, aiohttp, asyncio\n",
        "\n",
        "async def download_image(session: aiohttp.ClientSession, img_url: str) -> PIL.Image:\n",
        "  \"\"\"Returns a PIL.Image object from the provided URL.\"\"\"\n",
        "  async with session.get(img_url) as img_resp:\n",
        "    buffer = io.BytesIO()\n",
        "    buffer.write(await img_resp.read())\n",
        "    return PIL.Image.open(buffer)\n",
        "\n",
        "\n",
        "async def process_image(img_future: asyncio.Future[PIL.Image]) -> str:\n",
        "  \"\"\"Summarise the image using the Gemini API.\"\"\"\n",
        "  # This code uses a future so that it defers work as late as possible. Using a\n",
        "  # concrete Image object would require awaiting the download task before *queueing*\n",
        "  # this content generation task - this approach chains the futures together\n",
        "  # so that the download only starts when the generation is scheduled.\n",
        "  r = await client.aio.models.generate_content(\n",
        "      model=MODEL_ID,\n",
        "      contents=[prompt, await img_future]\n",
        "  )\n",
        "  return r.text"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "id": "447qlmtD2kWe"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Download and content generation queued for 3 images.\n",
            "\n",
            "Wild elephant family.\n",
            "\n",
            "Jetpack backpack concept\n",
            "\n",
            "Cat, person, tree.\n"
          ]
        }
      ],
      "source": [
        "async def download_and_describe():\n",
        "\n",
        "  async with aiohttp.ClientSession() as sesh:\n",
        "    response_futures = []\n",
        "    for img_filename in img_filenames:\n",
        "\n",
        "      # Create the image download tasks (this does not schedule them yet).\n",
        "      img_future = download_image(sesh, img_dir + img_filename)\n",
        "\n",
        "      # Kick off the Gemini API request using the pending image download tasks.\n",
        "      text_future = process_image(img_future)\n",
        "\n",
        "      # Save the reference so they can be processed as they complete.\n",
        "      response_futures.append(text_future)\n",
        "\n",
        "    print(f\"Download and content generation queued for {len(response_futures)} images.\")\n",
        "\n",
        "    # Process responses as they complete (may be a different order). The tasks are started here.\n",
        "    for response in asyncio.as_completed(response_futures):\n",
        "      print()\n",
        "      print(await response)\n",
        "\n",
        "\n",
        "await download_and_describe()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QOU0_lELKKFG"
      },
      "source": [
        "In the above example, a coroutine is created for each image that both downloads and then summarizes the image. The coroutines are executed in the final step, in the `as_completed` loop. To start them as early as possible without blocking the other work, you could wrap `download_image` in [`asyncio.ensure_future`](https://docs.python.org/3/library/asyncio-future.html#asyncio.ensure_future), but for this example the execution has been deferred to keep the creation and execution concerns separate."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-uAy7qOkOGHi"
      },
      "source": [
        "## Next Steps\n",
        "\n",
        "* Check out the [`AsyncClient`](https://googleapis.github.io/python-genai/genai.html#genai.client.AsyncClient) class in the Python SDK reference.\n",
        "* Read more on Python's [`asyncio`](https://docs.python.org/3/library/asyncio.html) library"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "Asynchronous_requests.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
